#include <stdio.h>
#include <stdlib.h>
#include <string.h>





//#define DEBUG

#ifdef DEBUG
    #define ASSERT(cond)                                                                        \
    if (!(cond))                                                                                \
    {                                                                                           \
        printf("The state is wrong: %s, file: %s, line: %d\n", #cond, __FILE__, __LINE__);      \
        abort();                                                                                \
    }

#else
    #define ASSERT(cond)
#endif //DEBUG


//#define _EJC

#ifdef  _EJC
    #define OUT if (0)
    #define PRINT_INFO

#else
    #define OUT
    #define PRINT_INFO                               \
    printf ("\nAvailable commands:\n"                \
            "1 - create stack\n"                     \
            "2 - push\n"                             \
            "3 - pop\n"                              \
            "4 - dump stack\n"                       \
            "5 - delete stack\n"                     \
            "9 - show available commands\n"          \
            "0 - exit\n\n")

#endif // _EJC

const int POiSON = 228;

enum
{
    EXIT      = 0,
    CREATE    = 1,
    PUSH      = 2,
    POP       = 3,
    DUMP      = 4,
    DESTRUCT  = 5,
    COMMANDS  = 9,
};


typedef struct stack_s
    {
        int* data;
        int  max_size;
        int  size_now;
    } stack;

stack* Stack_Construct (int stack_size);

void Stack_Destruct (stack* the_stack);

int Stack_OK (const stack* the_stack);

void Stack_Dump (stack* the_stack);

int Stack_Full (stack* the_stack);

int Stack_Empty (stack* the_stack);

void Stack_Push (stack* the_stack, int item);

int Stack_Pop (stack* the_stack);




int main()
{
    stack* test = {0};
    int size = 0;
    int push = 0;

    char command = 0;
    int exit = 0;
    int stack_created = 0;

    OUT printf ("# This program allows you to create a stack and do some staff with it\n"
                "# The developer:     Yura Gorishniy     <strausmg@gmail.com>\n"
                "# Version 1.00\n"
                "# The file: %s\n"
                "# The compilation time: %s, %s\n\n", strrchr (__FILE__, '\\'), __DATE__, __TIME__);

    PRINT_INFO;

    while (1)
    {
        while (isspace (command = getchar()));
        command -= 48;


        switch (command)
        {
            case CREATE:
                if (!stack_created)
                {
                    OUT printf ("\n" "Enter the length of your stack\n\n");
                    scanf ("%d", &size);
                    test = Stack_Construct (size);
                    stack_created = 1;
                }

                else printf ("\n" "You have already created a stack!\n");
                break;

            case PUSH:
                OUT printf ("\n" "Enter the number you want to push\n\n");
                scanf ("%d", &push);
                Stack_Push (test, push);
                printf ("\n");
                break;

            case POP:
                if (!stack_created)
                {
                    printf ("\n" "You haven't created a stack!\n");
                }
                else Stack_Pop (test);
                break;

            case DUMP:
                Stack_Dump (test);
                break;

            case DESTRUCT:
                Stack_Destruct (test);
                test = NULL;
                if (stack_created) OUT printf ("\n" "Your stack was deleted\n\n");
                stack_created = 0;
                break;

            case COMMANDS:
                PRINT_INFO;
                break;

            case EXIT:
                printf("\n" "Goodbye!\n");
                return 0;
                break;

            default:
                printf ("\n" "Your command doesn't exist, try again\n\n");
                break;
        }
    }

Stack_Dump (test);

Stack_Destruct (test);

return 0;
}





stack* Stack_Construct (int stack_size)
{
    stack* new_stack = (stack*) calloc (1, sizeof(stack));
    new_stack -> data = (int*)  calloc (stack_size, sizeof(int));
    new_stack -> size_now = 0;
    new_stack -> max_size = stack_size;

return new_stack;
}




void Stack_Destruct (stack* the_stack)
{
    if (!the_stack) printf ("\n" "You haven't created a stack!\n\n");
    else
    {
        the_stack -> size_now = -1;

        int i = 0;

        for (i = 0; i < the_stack -> max_size; ++i)
        {
            ASSERT (0 <= i && i < max_size);
            the_stack -> data[i] = POiSON;
        }

        free (the_stack -> data);
        free (the_stack);
    }
}




int Stack_OK (const stack* the_stack)
{
    return  (  the_stack != NULL
            && the_stack -> size_now >= 0
            && the_stack -> size_now <= the_stack -> max_size - 1
            && the_stack -> data != NULL) ?
    1 : 0;
}




void Stack_Dump (stack* the_stack)
{
    if (!the_stack)
    {
        printf ("\n" "You haven't created a stack!\n");
    }

    else
    {
        if (Stack_OK (the_stack))         printf ("\n" "Your stack [%.8x] is ok\n\n",     the_stack);
        else                              printf ("\n" "Your stack [%.8x] is NOT ok\n\n", the_stack);

        printf ("max_size = %d\n" "size_now = %d\n\n", the_stack -> max_size, the_stack -> size_now);

        int i = 0;
        for (i = 0; i < the_stack -> max_size; ++i)
        {
            printf ("data[%d] = %d", i, the_stack -> data[i]);
            printf (i < the_stack -> size_now ? " *\n" : "\n");
        }

        printf ("\n\n");
    }
}




int Stack_Full (stack* the_stack)
{
    return (the_stack -> size_now == the_stack -> max_size) ? 1 : 0;
}




int Stack_Empty (stack* the_stack)
{
    return (the_stack -> size_now == 0) ? 1 : 0;
}




void Stack_Push (stack* the_stack, int item)
{
    if (Stack_OK  (the_stack))
    {
        ASSERT (   0 <= the_stack -> size_now
                    && the_stack -> size_now <= the_stack -> max_size - 1);

        the_stack -> data[the_stack -> size_now] = item;


        the_stack -> size_now += 1;
    }

    else Stack_Dump (the_stack);
}




int Stack_Pop (stack* the_stack)
{
    int pop = 0;

    if (the_stack -> size_now > 0)
    {
        ASSERT (   0 <= the_stack -> size_now - 1
                && the_stack -> size_now <= the_stack -> max_size);

        pop = the_stack -> data[the_stack -> size_now - 1];


        ASSERT (   0 <= the_stack -> size_now - 1
                && the_stack -> size_now <= the_stack -> max_size);

        the_stack -> data[the_stack -> size_now - 1] = 0;


        the_stack -> size_now -= 1;

        if (!Stack_OK (the_stack)) printf ("Something went wrong");
    }

    else
    {
        printf ("\n" "There is nothing to pop from your stack\n");
        return 0;
    }

return pop;
}
